/**
 * Created by Sean Patrick Floyd (<a
 * href="mailto:sean@mostlymagic.com">sean@mostlymagic.com</a>) on 20.05.2010.
 */
package com.google.code.imds4j.impl.map;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;

import com.google.code.imds4j.api.DataStructure;
import com.google.code.imds4j.api.Query;

// TODO: synchronize map access
public class MapDataStructure<K, V> implements DataStructure<K, V> {

    private final Map<K, Collection<V>> map = new HashMap<K, Collection<V>>();

    @Override
    public Query<V> and(final Query<V> first, final Query<V> second) {
        return new AndQuery<V>(first, second);
    }

    @Override
    public Collection<K> keys() {
        return new HashSet<K>(this.map.keySet());
    }

    // TODO: there has got to be a better solution than this
    @Override
    public Query<V> not(final Query<V> inner) {
        return new NotQuery<K, V>(inner, this);
    }

    @Override
    public Query<V> or(final Query<V> first, final Query<V> second) {
        return new OrQuery<V>(first, second);
    }

    @Override
    public void put(final V value, final K... keys) {
        for (final K key : keys) {
            if (!this.map.containsKey(key)) {
                this.map.put(key, new HashSet<V>());
            }
            this.map.get(key).add(value);
        }

    }

    @Override
    public void removeKey(final K key) {
        this.map.remove(key);
    }

    @Override
    public void removeValue(final V value) {
        final Collection<K> keysToRemove = new HashSet<K>();
        for (final Entry<K, Collection<V>> entry : this.map.entrySet()) {
            final Collection<V> rowValue = entry.getValue();
            if (rowValue.remove(rowValue) && rowValue.isEmpty()) {
                keysToRemove.add(entry.getKey());
            }
        }
        for (final K key : keysToRemove) {
            this.map.remove(key);
        }
    }

    @Override
    public Query<V> search(final K key) {
        return new SearchQuery<K, V>(key, this);
    }

    @Override
    public Collection<V> values() {
        final Set<V> set = new HashSet<V>();
        for (final Collection<V> row : this.map.values()) {
            set.addAll(row);
        }
        return set;
    }

    @Override
    public Collection<V> values(final K key) {
        return new HashSet<V>(this.map.containsKey(key) ? this.map.get(key)
                : Collections.<V> emptySet());
    }

}